vorheriges KapitelInhaltsverzeichnisStichwortverzeichnisFeedbacknächstes Kapitel


Woche 3

Tag 16


Perl für CGI-Skripts

In den letzten Jahren hat sich die Erstellung von CGI-Skripten zu einem der Hauptanwendungsbereiche von Perl entwickelt. CGI ist das Akronym für »Common Gateway Interface« und bezieht sich auf Programme und Skripts, die auf einem Webserver stehen und ausgeführt werden als Antwort auf entsprechende Eingaben eines Webbrowsers. Diese Eingaben können die Form von Formularen, komplexen Links oder bestimmten Arten von Image-Maps annehmen - ja eigentlich bedarf alles, was als Antwort nicht gerade eine ganz gewöhnliche Datei ist, einer Art von CGI- Skript.

Aufgrund der Popularität von Perl als CGI-Sprache möchte ich nicht darauf verzichten, Ihnen im Zusammenhang mit Perl eine kurze Einführung in CGI zu geben. Heute werden wird das bisher Erlernte anwenden, um CGI-Skripts für das Web zu erstellen. Im einzelnen erfahren Sie

Bevor Sie starten

Um in Perl ein CGI-Skript zu schreiben, benötigen Sie drei Dinge:

Sie benötigen nicht unbedingt das Modul CGI.pm, um CGI-Skripts mit Perl zu erstellen. Es gibt auch andere Hilfsskripte, mit denen Sie CGI programmieren können, oder Sie schreiben den ganzen zugrundeliegenden Code gleich selbst. Dies würde jedoch einen erheblich größeren Programmieraufwand bedeuten; es ist viel, viel einfacher, statt dessen CGI.pm einzusetzen. Wir tun es alle. Warum schließen Sie sich uns nicht an?

Aufgrund der großen Bandbreite an Webservern für verschiedene Plattformen und den Unterschieden zwischen ihnen, möchte ich der Diskussion, wie Sie Ihren Webserver für CGI fit machen, nicht allzuviel Platz einräumen. Umfangreiche Hilfe finden Sie in den zahlreichen Hilfedateien im Web:

Sie können auch jederzeit die Dokumentation, die mit Ihrem Webserver ausgeliefert wurde, zu Rate ziehen. Und wenn Sie vorhaben, wirklich viel mit CGI zu arbeiten, sollten Sie vielleicht die Anschaffung von Sams Teach Yourself CGI Programming in a Week von Rafe Colburn in Erwägung ziehen. Das Buch wird Ihnen eine größere Hilfe sein und mehr Beispiele bieten, als ich es im Rahmen dieses Buches kann.

Um mit dem Rest dieser Lektion nicht allzu viele Schwierigkeiten zu haben, sollten Sie zumindest einige flüchtige Kenntnisse in HTML haben. Auch hier möchte ich Ihnen empfehlen, sich durch die Vielzahl der HTML-Tutorien im Web zu kämpfen oder das Buch Sams Teach Yourself Web Publishing with HTML 4 in 21 Days von Laura Lemay (das bin übrigens ich) durchzuarbeiten.

Allgemeines zu CGI

Beginnen wir diesen Abschnitt damit, dass ich Ihnen etwas Hintergrundwissen zu CGI vermittle und Ihnen erkläre, wo CGI in der Beziehung zwischen Webbrowser und Webserver einzuordnen ist.

CGI steht, wie bereits erwähnt, für Common Gateway Interface. Common (»gemeinsam, allgemein«) bedeutet, dass der gleiche Prozeß für viele verschiedene Arten von Webservern verwendet wird, und Gateway (»Tor«) resultiert daher, dass die Skripts früher einmal in der Regel als Tor zwischen dem Webserver und einem größeren Programm fungierten - zum Beispiel einer Datenbank oder einer Suchmaschine. Heutzutage hat CGI viel von seiner ursprünglichen Bedeutung verloren und bezieht sich einfach auf ein Skript oder Programm, das als Antwort auf eine Eingabe eines Webbrowsers ausgeführt wird.

Es gibt viele Probleme, die sich mit einem CGI-Skript lösen lassen, und es gibt für jedes Problem unzählige Möglichkeiten, das zugehörige Skript zu schreiben. Im Rahmen dieser Lektion konzentrieren wir uns auf einen typischen Anwendungsbereich von CGI-Skripten: die Bearbeitung von Daten, die als Teil eines HTML-Formulars empfangen wurden. In Abbildung 16.1 sehen Sie ein typisches Ablaufdiagramm, das zeigt, was passiert, wenn ein Benutzer über seinen Webbrowser ein Formular anfordert, es ausfüllt und wieder zurückschickt.

Abbildung 16.1:  Der CGI-Prozeß

Und so sehen die Schritte im einzelnen aus:

Das scheint nicht allzu kompliziert. Wichtig ist, dass Sie verstanden haben, wo das CGI-Skript in dem Datenfluß eingreift, woher die Daten für das CGI-Skript kommen und wohin sie gehen. Später wird dies noch von Bedeutung sein.

Ein CGI-Skript erstellen, vom Formular bis zur Antwort

Am besten erlernt man die CGI-Programmierung, indem man einfach loslegt - also, auf geht's! In diesem Abschnitt erstellen wir ein ganz einfaches CGI-Skript - das Gegenstück zu »Hallo Welt«. Dazu benötigen wir ein einfaches HTML-Formular, Perl für das CGI-Skript und das Modul CGI.pm, um alles zusammenzubinden. Das HTML- Formular gebe ich Ihnen vor, das Skript werden wir Zeile für Zeile erarbeiten.

Das Formular

In Listing 16.1 finden Sie den HTML-Code für ein einfaches HTML-Formular, das Sie nach Ihrem Namen fragt. In Abbildung 16.2 können Sie dann sehen, wie das Ganze auf dem Webbrowser ausgegeben wird.

Listing 16.1: Die Datei name.html

1:  <HTML>
2: <HEAD>
3: <TITLE>Tell Me Your Name</TITLE>
4: </HEAD>
5: <BODY>
6: <FORM ACTION="/cgi-bin/name.pl">
7: <P>Geben Sie Ihren Namen ein: <INPUT NAME="name">
8: <P><INPUT TYPE="SUBMIT" VALUE="Abschicken!">
9: </FORM>
10: </BODY>
11: </HTML>

Zwei wichtige Dinge möchte ich zu diesem HTML-Code anmerken:

Einige Webserver verlangen, dass Sie Ihre Skripts mit der Extension .cgi versehen, damit sie als CGI-Skripts erkannt werden. Da dies von Server zu Server unterschiedlich gehandhabt wird, sollten Sie vorsichtshalber Ihre Server-Dokumentation konsultieren.

Das Skript

Kommen wir jetzt zu dem Perl-Skript, das die Daten verarbeitet. Ein CGI-Skript wird in Perl grundsätzlich in der gleichen Weise aufgesetzt wie ein normales Perl-Skript, das von der Befehlszeile aus ausgeführt wird. Es gibt jedoch einige wesentliche Unterschiede, die darauf beruhen, dass Ihr Skript vom Webserver aufgerufen wird und nicht von Ihnen. So erhalten Sie zum Beispiel keine Optionen oder Dateinamen- Argumente über die Befehlszeile. Alle Daten, die Sie im Skript erhalten, kommen vom Webserver (oder werden von Ihnen selbst aus Dateien auf der Festplatte eingelesen). Die Ausgabe Ihres Skripts muss in einem bestimmten Format vorliegen - normalerweise HTML.

Zum Glück gibt es das Modul CGI.pm, geschrieben von Lincoln Stein, das mit der aktuellen Perl-Version ausgeliefert wird, Ihnen die CGI-Skripterstellung leichter macht und einen großen Teil der Probleme abfängt.

Lassen Sie uns mit den obersten Zeilen unseres CGI-Skripts anfangen:

#!/usr/bin/perl -w
use strict;
use CGI qw(:standard);

Die shebang-Zeile und use strict kennen Sie bereits, und die dritte Zeile use CGI sollte Sie auch nicht allzusehr überraschen. Das Tag :standard importiert, wie Sie sich vielleicht aus Kapitel 13, »Gültigkeitsbereiche, Module und das Importieren von Code«, erinnern, nur einen Teilbereich des CGI-Moduls und nicht das ganze Modul. Dieses Tag werden Sie wahrscheinlich im Zusammenhang mit CGI.pm häufig verwenden.

Die meisten CGI-Skripts bestehen aus zwei Teilen: Der erste Teil liest die Daten ein, die Sie von einem Formular oder dem Webbrowser erhalten haben, und verarbeitet sie. Der zweite Teil gibt eine Antwort aus, die in der Regel im HTML-Format erzeugt wird. Da es in unserem Beispiel kaum etwas zu verarbeiten gibt, gehen wir gleich zum Ausgabeteil über.

Das erste, was wir ausgeben müssen, ist ein besonderer Header an den Webserver, der ihm mitteilt, welche Art von Datei Sie zurücksenden. Senden Sie eine HTML- Datei zurück, lautet der Typ text/html. Ist es eine einfache Textdatei, lautet der Typ text/plain. Für eine Grafik ist es image/gif. Diese Dateitypen sind alle als Teil der MIME-Spezifikation standardisiert, und wenn Sie tiefer in die CGI-Skripterstellung einsteigen, werden Sie sich mit zumindest einigen dieser Formate näher vertraut machen müssen. Das geläufigste Format ist zweifelsohne text/html. CGI.pm stellt Ihnen eine grundlegende Subroutine namens print_header() zur Verfügung, die den entprechenden Header für diesen Formattyp ausgibt.

print header();

Alle weiteren Ausgaben, die auf den Header folgen, müssen jetzt im HTML-Format sein. Sie können entweder reine HTML-Tags ausgeben, die Perl-Subroutinen von CGI.pm verwenden, um den HTML-Code zu erzeugen, oder beide Methoden miteinander kombinieren. Da ich mit HTML vertraut bin, ziehe ich die Subroutinen von CGI.pm nur dort vor, wo es mir Tipparbeit erspart, und verwende ansonsten normale print-Anweisungen. Sehen Sie hier den Rest unseres einfachen CGI-Skripts:

print start_html('Hallo!');
print "<H1>Hallo, ", param('name'), "!</H1>\n";
print end_html;

Die erste Zeile ruft die CGI.pm-Subroutine start_html() auf, die den ersten Teil einer jeden HTML-Datei ausgibt (die Tags <HTML>, <HEAD>, <TITLE> und <BODY>). Das String-Argument für start_html() ist der Titel der Seite. Sie können dieser Subroutine aber auch andere Argumente übergeben, um zum Beispiel Hintergrundfarbe, Schlüsselwörter und andere Besonderheiten des Headers zu setzen (mehr dazu im nächsten Abschnitt).

Die zweite Zeile ist eine reguläre print-Anweisung, die eine HTML-Überschrift (<H1>- Tag) zum Hallo-Sagen ausgibt. Zwischen dem öffnenden und dem schließenden Tag befindet sich der interessante Teil. Die Subroutine param() ist ebenfalls Teil von CGI.pm und dient dazu, an die Informationen zu gelangen, die Ihr Benutzer in das Formular eingegeben hat. Rufen Sie dazu param() mit dem Namen des Formularelements (aus dem NAME-Attribut des HTML-Tags des Formularelements) auf, und die Routine liefert den Wert zurück, den der Benutzer für das Element eingegeben hat. Mit dem Aufruf von param('name') erhalten wir den String, den der Benutzer in das Textfeld unseres Formulars eingegeben hat. Mit diesem Wert können wir dann die Antwort erzeugen.

Die dritte Zeile ist eine weitere Subroutine von CGI.pm, die lediglich die abschließenden HTML-Elemente (</BODY> und </HTML>) ausgibt und damit die Antwort komplettiert. Sehen Sie im folgenden das komplette Skript:

#!/usr/bin/perl -w
use strict;
use CGI qw(:standard);

print header;
print start_html('Hallo!');
print "<H1>Hallo, ", param('name'), "!</H1>\n";
print end_html;

Vielleicht ist Ihnen aufgefallen, dass wir für die Ausgabe eigentlich keine besonderen Schritte unternommen haben. Wir haben lediglich einfache print-Anweisungen verwendet. Und doch geht die Ausgabe nicht an den Bildschirm, sondern zurück an den Browser. Damit ist ein CGI-Skript ein Paradebeispiel dafür, dass die Standardeingabe und -ausgabe nicht unbedingt die Tastatur oder der Bildschirm sein müssen. CGI-Skripts lesen Ihre Eingaben von der Standardeingabe und schreiben ihre Ausgabe in die Standardausgabe, nur das in diesem Falle die Standardeingabe und - ausgabe der Webserver ist. Sie müssen keine besonderen Vorkehrungen treffen, um mit dem Server zu kommunizieren; über die Standardwege klappt das reibungslos.

Was aber ist unsere Ausgabe? Wenn der Benutzer in das Formular Fred eingegeben hätte, würde die Ausgabe des CGI-Skripts samt Header und HTML-Daten wie folgt aussehen:

Content-type: text/html

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<HTML><HEAD><TITLE>Hallo!</TITLE>
</HEAD><BODY><H1>Hallo, Fred!</H1>
</BODY></HTML>

Diese Daten werden über die Standardausgabe an den Webserver übergeben, der sie wiederum an den Webbrowser von Fred zurückgibt.

Das Skript testen

Bevor Sie ein Skript auf Ihren Webserver installieren, ist es angebracht, das Skript zu testen, um sicherzustellen, dass Sie keine groben Fehler gemacht haben. Mit Hilfe von CGI.pm können Sie das CGI-Skript zum Test von der Befehlszeile aus starten:

% name.pl
(offline mode: enter name=value pairs on standard input)

Nach dieser Zeile können Sie die Formulareingabe in Form von Name/Werte-Paaren simulieren. So müßten Sie zum Beispiel für das Hallo-Formular als Name name angeben, und der Wert wäre dann irgendein Name (zum Beispiel Fred). Ihre Eingabe sähe dann wie folgt aus:

name=Fred

Solange Ihre Eingabe keine Leerzeichen enthält, müssen Sie keine Anführungszeichen setzen. Nachdem Sie Ihre Name/Werte-Paare eingegeben haben, drücken Sie [Strg]+[D] ([Strg]+[Z] für Windows), um die Standardeingabe zu beenden. Das Skript wird dann ausgeführt, als ob es die Eingabe vom Formular erhalten hätte, und das Ergebnis wird auf dem Bildschirm ausgegeben.

Alternativ können Sie die Namen und Werte auch als Argumente in der Befehlszeile des Skripts eingeben:

% name.pl name=Fred

Das Skript wird dann diese Name/Werte-Paare als Eingabe betrachten und Sie nicht nach weiteren Eingaben fragen.

Nachdem Sie sich davon überzeugt haben, dass Ihr CGI-Skript so funktioniert, wie Sie es erwarten, besteht der letzte Schritt darin, das Skript auf dem Webserver zu installieren. Installation kann bedeuten, dass Sie Ihr Skript in einem besonderen Verzeichnis namens cgi-bin ablegen, die Skriptextension in .cgi umbenennen oder auf anderweitige Art und Weise dem Webserver signalisieren, dass es sich bei Ihrem Skript um ein CGI-Skript handelt. (Auch hier gilt, dass sich diese Anforderungen von Server zu Server unterscheiden können, so dass Sie auf alle Fälle in Ihrer Server- Dokumentation über Einzelheiten informieren sollten). Eventuell müssen Sie auch sicherstellen, dass Ihr Skript eine ausführbare Datei ist, oder spezielle Zugriffsberechtigungen vergeben. Und abschließend werden Sie noch die Original- HTML-Datei ändern müssen, so dass sie auf die aktuelle Position des Skripts zeigt.

Haben Sie all diese Probleme gelöst, sollte es Ihnen möglich sein, in die HTML-Datei mit dem Formular einen Namen einzugeben, den Abschicken-Schalter anzuklicken und vom CGI-Skript eine Antwort zu erhalten. In Abbildung 16.3 sehen Sie die Antwort als Webseite.

Abbildung 16.3:  Hallo, die Antwort

CGI-Skripts mit CGI.pm entwickeln

Das Kernstück eines jeden CGI-Skripts ist das Modul CGI.pm. Sie können CGI-Skripts zwar auch in reinem Perl schreiben oder andere im Web verfügbare CGI-Programme verwenden, doch CGI.pm wird als Teil von Perl mit ausgeliefert, findet breite Unterstützung, ist robust, läuft plattformübergreifend und bietet Ihnen alles, was Sie bei Ihrer Arbeit mit CGI gebrauchen könnten. Auf CGI.pm zu verzichten, heißt sich das Leben als Entwickler von CGI-Skripten unnötig schwer zu machen.

Im vorangehenden Abschnitt habe ich Ihnen ein wirklich einfaches Beispiel für den Einsatz von CGI.pm gezeigt. In diesem Abschnitt möchte ich Sie mit dem Inhalt dieses Moduls näher vertraut machen, so dass Sie hinterher wissen, was Sie alles damit anfangen können.

Woher bekommt man das CGI.pm-Modul?

Wenn Sie eine aktuelle Version von Perl haben, stehen die Chancen gut, dass CGI.pm dabei ist. Sind Sie sich nicht sicher, können Sie in dem lib-Verzeichnis Ihrer Perl- Installation nachschauen, ob das Modul dort abgelegt wurde. Ist es nicht vorhanden, können Sie es von der Website unter http://www.genome.wi.mit.edu/ftp/pub/ software/WWW/cgi_docs.html herunterladen; es ist aber auch als Teil von CPAN verfügbar. Auf der CGI.pm-Seite finden Sie genau erklärt, wie Sie bei der Installation dieser Datei vorzugehen haben, aber kurz gesagt, müssen Sie dazu lediglich die Datei CGI.pm in Ihr Perl-Verzeichnis lib kopieren.

Das CGI.pm-Paket enthält neben CGI.pm noch ein weiteres Modul namens Carp. Carp gehört in das CGI-Verzeichnis unter dem lib-Verzeichnis von Perl (falls es noch nicht dort abgelegt wurde). Carp wird verwendet, um hilfreiche Fehlermeldungen in Ihren Webserver-Protokollen oder dem Webbrowser auszugeben. Für das Debuggen von CGI-Skripten ist das Modul von unschätzbarem Wert, was Sie schnell feststellen werden, wenn Sie später einmal intensiver in die Arbeit mit CGI einsteigen (weiter unten erfahren Sie gleich noch etwas mehr über Carp).

Sowohl zu CGI.pm als auch zu Carp.pm gibt es eine Dokumentation im POD-Format. Um sich diese anzuschauen, brauchen Sie nur perldoc CGI einzutippen (MacPerl- Benutzer können die shuck-Anwendung nutzen). Sie finden die aktuelle Dokumentation aber auch im Web unter http://www.genome.wi.mit.edu/ftp/pub/ software/WWW/cgi_docs.html.

CGI.pm verwenden

Um CGI.pm in Ihren Perl-Skripten zu verwenden, importieren Sie es wie jedes andere Modul. CGI.pm weist mehrere Import-Tags auf, die Sie verwenden können, unter anderem:

CGI.pm ist so implementiert, dass Sie es sowohl objektorientiert verwenden können (unter Verwendung eines CGI-Objekts dessen Methoden man aufruft) als auch durch Aufruf einfacher Subroutinen. Wenn Sie einen der oben angeführten Import-Tags verwenden, stehen Ihnen die Subroutinennamen in Ihrem Skript zur Verfügung. Wenn Sie keine Import-Tags verwenden, wird davon ausgegangen, dass Sie die objektorientierte Version von CGI.pm nutzen möchten.

Formulareingaben verarbeiten

Der vielleicht größte Nutzen, den Ihnen CGI.pm bietet, besteht darin, dass dieses Modul Formulareingaben verarbeitet, die der Webbrowser an den Webserver schickt und der Server an das Skript weiterleitet. Diese Eingaben kommen vom Browser in einer ganz besonderen codierten Form, manchmal über die Standardeingabe und manchmal als Schlüsselargumente mit nichtalphanumerischen Zeichen im Hex-Code. Wenn Sie Ihren eigenen CGI-Prozessor von Grund auf selbst schreiben wollten, müßten Sie sich um die Decodierung selbst kümmern (und, glauben Sie mir, das macht keinen allzu großen Spaß). Mit CGI.pm bleibt Ihnen all das erspart, und Sie müssen sich lediglich um die tatsächlichen Eingabewerte kümmern, um die es ja auch eigentlich geht.

Die Eingabe, die Sie von einem Formular erhalten, besteht aus Schlüssel/Wert- Paaren. Der Schlüssel ist der Name des Formularelements (im HTML-Code durch das NAME-Attribut definiert), der Wert ist das, was der Benutzer letztendlich eingegeben, ausgewählt oder im Formular markiert hat. Der Wert, den Sie erhalten, hängt vom Typ des Formularelements ab. Einige Formularelemente - wie zum Beispiel Textfelder - liefern einfache Strings, andere - zum Beispiel Markierungsfelder - liefern nur Ja oder Nein. Popup-Menüs und scrollbare Listen, die mit dem HTML-Tag <SELECT> erzeugt wurden, können sogar mehrere Werte enthalten.

Das CGI.pm-Modul speichert diese Schlüssel und Werte in einem Parameter-Array. Mit der Subroutine param() können Sie auf die Elemente im Array zugreifen. Ohne irgendwelche Argumente liefert param() nur eine Liste der Schlüssel im Parameter- Array zurück (die Namen der Formularelemente). Wenn das CGI-Skript sowohl die ursprüngliche HTML-Seite mit dem Formular als auch das Ergebnis erzeugt, kann man anhand dieser Liste feststellen, ob das Formular vollständig ausgefüllt wurde. Auch beim Debuggen lohnt es sich param() ohne Argumente aufzurufen, um die Schlüssel und Werte des Formulars auszugeben. Der Code dazu würde folgendermaßen lauten:

foreach $key (param()) {
print "$key hat den Wert ", param($key), "\n";
}

Beachten Sie, dass die Parameter im Array in der gleichen Reihenfolge stehen, wie sie vom Browser ursprünglich gesendet wurden. In den meisten Fällen entspricht das der Reihenfolge, in der sie auf der Seite erscheinen. Da es jedoch dafür keine Garantie gibt, sind Sie am besten beraten, wenn Sie auf jedes Formularelement explizit Bezug nehmen - falls Ihnen die Reihenfolge wichtig ist.

Wird die Subroutine param() mit dem Namen eines Formularelements als Argument aufgerufen, liefert Sie entweder den Wert dieses Formularelements zurück oder undef, wenn zu diesem Formularelement kein Wert geschickt wurde. Auf diese Art und Weise werden Sie param() wahrscheinlich am häufigsten in Ihren CGI-Skripten aufrufen. Der Schlüssel, den Sie als Argument für param() verwenden, muss mit dem Namen des Formularelements in der HTML-Datei genau übereinstimmen. Um zum Beispiel den Wert eines Textfeldes zu erhalten, das als <INPUT NAME="foozle"> definiert wurde, müssen Sie param('foozle') eingeben. Meistens erhalten Sie einen einfachen skalaren Wert als Antwort. Manche Formularelemente, bei denen Mehrfachauswahl möglich ist, geben jedoch eine Liste der gewählten Optionen zurück. Es ist Ihre Aufgabe, die verschiedenen Werte, die Sie aus einem Formular zurückerhalten, in dem CGI-Skript zu verarbeiten.

HTML-Code erzeugen

Ein Großteil des CGI-Skripts besteht in der Regel aus dem HTML-Code, der für die Antwort erzeugt wird. Die Skripts, die wir für CGI schreiben, enthalten wahrscheinlich mehr print-Anweisungen als alle anderen Skripts, die wir bisher geschrieben haben.

Um eine HTML-Ausgabe zu erzeugen, geben Sie einfach, wie bei jedem anderen Skript, die auszugebenden Zeilen weiter an die Standardausgabe. Sie können dazu mehrere Wege einschlagen:

Ausgabe mit print

Für den ersten Weg rufen Sie print einfach mit dem auszugebenden HTML-Code auf - so wie Sie es bereits die ganze Zeit gemacht haben:

print "<HTML><HEAD><TITLE>Dies ist eine Webseite</TITLE></HEAD>\n";
print "<BODY BGCOLOR=\"white\">\n";
# und so weiter

Ausgabe mit Hier-Dokumenten

print-Anweisungen bereiten zwar keine Probleme, können aber manchmal recht unhandlich sein, besonders wenn die Ausgabe recht umfangreich ist oder Sie es mit verschachtelten Anführungszeichen zu tun haben (wie bei dem Wert »white« im obigen Beispiel). Wenn Sie einen größeren HTML-Block auszugeben haben, können Sie von einer speziellen Perl-Option, den sogenannten »Hier-Dokumenten«, Gebrauch machen. Der Name mutet seltsam an, besagt aber einfach: »Gib alles bis hier aus.« Ein Hier-Dokument könnte zum Beispiel folgendermaßen aussehen:

print <<EOF;
Diese Zeilen werden so ausgegeben
wie sie hier erscheinen; Sie benötigen keine zusätzlichen
print-Anweisungen, "Anführungszeichen mit Escape-Zeichen"
oder besonderen Neue-Zeile-Zeichen. So wie Sie es hier sehen.
EOF

Dieser kurze Perl-Code würde folgendes Ergebnis erzeugen:

Diese Zeilen werden so ausgegeben
wie sie hier erscheinen; Sie benötigen keine zusätzlichen
print-Anweisungen, "Anführungszeichen mit Escape-Zeichen"
oder besonderen Neue-Zeile-Zeichen. So wie Sie es hier sehen.

Mit anderen Worten, der ausgegebene Text entspricht fast genau dem Text in dem Skript. Die am Anfang stehende print-Anweisung im Hier-Dokument legt fest, wie weit gelesen und ausgegeben werden soll. Zur Kennzeichnung des Endes des Hier- Dokuments wird ein Endemarker definiert, bei dem es sich entweder um ein Wort, das in der Sprache keine anderweitige Bedeutung hat, oder um einen String in Anführungszeichen handeln kann. Ich habe in obigem Beispiel EOF (End of File für »Ende der Datei«) gewählt, da dies eine nette, kurze und allgemein übliche Abkürzung ist, die sich gut vom Rest des Skripts abhebt.

Wenn Sie den Endemarker in Anführungszeichen setzen, legt die Art der Anführungszeichen fest, wie der Text innerhalb des Hier-Dokuments bearbeitet wird. Ein einfaches Wort, wie das von mir oben verwendete EOF, erlaubt Variableninterpolation - so als ob der Text innerhalb des Hier-Dokuments in doppelten Anführungszeichen stehen würde. Den gleichen Effekt erzielen Sie, wenn Sie das Wort in doppelte Anführungszeichen setzen. Ein in einfache Anführungszeichen geklammerter Endemarker unterdrückt die Variableninterpolation wie bei allen Strings in einfachen Anführungszeichen.

Das Ende eines Hier-Dokuments wird von dem Endemarker (Wort oder String) angezeigt, mit dem das Hier-Dokument gestartet wurde, allerdings ohne die Anführungszeichen. Der Endemarker steht allein auf einer Zeile ohne irgendwelche führenden oder angehängten Zeichen oder Whitespaces. Nach dem Endemarkierer können Sie ein weiteres Hier-Dokument starten, wieder auf print-Anweisungen zurückgreifen oder beliebige anderen Zeilen in Perl-Code schreiben.

Weitere Informationen zu den Hier-Dokumenten finden Sie in der perldata-Manpage.

Ausgabe mit CGI.pm-Subroutinen

Die dritte Möglichkeit, HTML-Code in einem CGI-Skript zu erzeugen, ist die Verwendung der dafür vorgesehenen CGI.pm-Subroutinen. Für die meisten HTML- Tags gibt es in CGI.pm äquivalente Perl-Subroutinen. Diese CGI.pm-Subroutinen haben den Vorteil, dass Sie auf diese Weise Variablenreferenzen einfügen (mittels Strings in doppelten Anführungszeichen) und schnell bestimmte HTML-Elemente, wie zum Beispiel Formularelemente, erzeugen können. Außerdem verfügt CGI.pm über die Subroutinen start_html() und end_html(), mit denen Anfang und Ende einer HTML-Datei ausgegeben werden. Alle Subroutinen zur Erzeugung von HTML-Code liefern Strings zurück. Um diese Strings tatsächlich auszugeben, müssen Sie sie einer print-Anweisung übergeben.

Manche Subroutinen erzeugen einzelne Tags, die keine Argumente übernehmen (zum Beispiel p() und hr(), die <P> und <HR> erzeugen). Andere wiederum erzeugen paarweise öffnende und schließende Tags. Tags dieser Art übernehmen ein oder mehrere String-Argumente für den Text zwischen das öffnende und das schließende Tag.

Subroutinen können ineinander verschachtelt werden:

h1('Dies ist eine Überschrift');    # <H1>Dies ist eine Überschrift</H1>
b('Bold'); # <B>Bold</B>
b('mal fett mal ', i('kursiv')); # <B>mal fett mal <I>kursiv</I></B>
ol(
li('erstes Element'),
li('zweites Element'),
li('drittes Element'),
);

Wenn das auszugebende HTML-Tag selbst Attribute enthält, stellen Sie diese in geschweifte Klammern {} und trennen Sie Name und Wert des Attributs durch die Zeichen => (wie bei einem Hash):

a({href=>"index.html", name=>"foo"}, "Homepage");
# <A HREF="index.html" NAME="foo">Homepage</A>

Fast alle HTML-Tags, die Sie in einer HTML-Datei finden können, sind als Subroutine verfügbar. Für welche Tags Ihnen Subroutinen zur Verfügung stehen, hängt allerdings davon ab, welche Gruppe von Subroutinen Sie in die use CGI-Zeile importieren. CGI.pm enthält außerdem einen besonders robusten Satz an Subroutinen zur Erzeugung von weiteren Formularelementen. Ich kann hier leider nicht auf alle einzeln eingehen, deshalb möchte ich Sie auf die CGI.pm-Dokumentation verweisen.

Das Ergebnis debuggen

Im Verlauf unseres Hallo-Beispiels haben Sie bereits eine Möglichkeit kennengelernt, wie man Skripts vor der Installation auf dem Server debuggt. Dabei haben wir die CGI-Eingabe als Name/Wert-Paar entweder über die Skript-Befehlszeile oder über die Standardeingabe eingegeben. Dieser Mechanismus ist unschätzbar, wenn es darum geht, kleinere Fehler aufzudecken, die sich beim Schreiben der CGI-Skripts eingeschlichen haben. Wenn Sie das Skript von der Befehlszeile ausführen, können Sie auch den Perl-Debugger verwenden, um sicherzustellen, dass Ihr Skript ordnungsgemäß läuft, bevor es installiert wird.

Irgendwann ist es jedoch soweit, dass Sie das CGI-Skript installieren und vor Ort ausführen müssen, um seine ordnungsgemäße Funktionsweise sicherzustellen. Wenn das Skript erst einmal installiert ist, kann das Debuggen allerdings Schwierigkeiten bereiten, da Fehler normalerweise in wenig hilfreichen Meldungen (wie Server Error 500) dem Browser mitgeteilt werden oder in Fehlerprotokollen landen, die weder Identifizierer noch Zeitmarkierungen haben, anhand derer man sehen könnte, welcher Fehler auf Sie zurückgeht.

An dieser Stelle kommt das Modul CGI::Carp ins Spiel. CGI::Carp ist Bestandteil von CGI.pm und sollte deshalb wie letzteres Modul auch Teil Ihrer Standard-Perl-Version sein. (Überprüfen Sie das, indem Sie in dem CGI-Unterverzeichnis des Perl- Verzeichnisses lib danach suchen. Es gibt auch noch ein reguläres Carp-Modul, das verwandt, aber nicht dasselbe ist.) Carp wird verwendet, um Fehlermeldungen für CGI- Skripts zu erzeugen, die Ihnen dann beim Debuggen dieser Skripts sehr dienlich sein können. Vor allem das Schlüsselwort fatalsToBrowser kann für das Debuggen sehr nützlich sein, da es alle Perl-Fehler in dem CGI-Skript als HTML-Code ausgibt, der als Antwort auf das abgeschickte Formular in dem Browser angezeigt wird, der wiederum das Formular angefordert hat. Um diese Fehler als Echo an den Browser zu schicken, fügen Sie folgende use-Zeile in den Anfang Ihres Skripts auf:

use CGI::Carp qw(fatalsToBrowser);

Abgesehen von fatalsToBrowser enthält das CGI::Carp-Modul neue Definitionen für die Funktionen warn() und die() (und fügt die Subroutinen croak(), carp() und confess() hinzu), so dass Fehler mit vernünftigen Identifizierern ausgegeben werden und im dem Fehlerprotokoll Ihres Webservers erscheinen. (Näheres zu croak(), carp() und confess() finden Sie in der Dokumentation zum Standard-Carp-Modul.) Zusammengefaßt läßt sich sagen, dass CGI::Carp besonders nützlich zum Debuggen Ihrer CGI-Skripts auf dem Server ist.

Ein Beispiel: Umfrage

Unser »Hallo Welt«-Beispiel zu Beginn hat Ihnen vielleicht einen Vorgeschmack darauf gegeben, wie sich CGI-Skripts einsetzen lassen. Um wirklich von Nutzen zu sein, ist es aber zu einfach. Deshalb wollen wir im folgenden ein wesentlich komplexeres Skript betrachten, das eine webbasierte Umfrage verarbeitet. Das Skript zeichnet alle aus der Umfrage stammenden Daten auf, indem es die Eingabe des aktuellen Formulars verarbeitet und daraus Ergebnistabellen erzeugt, die auf allen bis dato eingesandten Daten basieren. In Abbildung 16.4 sehen Sie das Formular unserer kleinen Umfrage.

Abbildung 16.4:  Eine einfache Web-Umfrage

Nachdem alle Fragen der Umfrage beantwortet wurden, verarbeitet das CGI-Skript diese Eingaben, fügt sie zu den bereits erhaltenen Daten hinzu und erzeugt eine Reihe von Tabellen, wie in Abbildung 16.5 zu sehen.

Abbildung 16.5:  Die Ergebnisse der Web-Umfrage

Alle Daten der Umfrage werden in einer separaten Datei auf dem Webserver gespeichert. Über das CGI-Skript werden Sie diese Datendatei öffnen, lesen und in die Datei schreiben.

Das Formular

Beginnen wir mit dem HTML-Code für das Formular, damit Sie lernen, mit welchen Werten Sie es in dem CGI-Skript zu tun bekommen. In Listing 16.2 sehen Sie diesen HTML-Code:

Listing 16.2: survey.html

<HTML>
<HEAD>
<TITLE>Kurze Umfage</TITLE>
</HEAD>
<BODY>
<H1>Nehmen Sie doch bitte an unserer Umfrage teil!</H1>
<FORM ACTION="/cgi-bin/survey.pl">
<P><STRONG>Alter: </STRONG><BR>
<INPUT TYPE="radio" NAME="age" VALUE="under18">Unter 18<BR>
<INPUT TYPE="radio" NAME="age" VALUE="18to34">18-34<BR>
<INPUT TYPE="radio" NAME="age" VALUE="35to50">35-50<BR>
<INPUT TYPE="radio" NAME="age" VALUE="50plus">50+
<P><STRONG>Geschlecht: </STRONG><BR>
<INPUT TYPE="radio" NAME="sex" VALUE="male">männlich<BR>
<INPUT TYPE="radio" NAME="sex" VALUE="female">weiblich
<P><STRONG>Sind Sie Perl-Programmierer? </STRONG><BR>
<INPUT TYPE="radio" NAME="perl" VALUE="yes">Ja<BR>
<INPUT TYPE="radio" NAME="perl" VALUE="no">Nein
<P><INPUT TYPE="submit" VALUE="Daten abschicken">
</FORM>
</BODY>
</HTML>

Einige wenige, aber wichtige Punkte möchte ich zu diesem HTML-Code anmerken. Im Gegensatz zum vorangehenden Beispiel finden Sie hier keine Textfelder, sondern Gruppen von Optionsfeldern (englisch: »radio buttons«). Beachten Sie, dass alle Optionsfelder einer Gruppe den gleichen Namen haben (so heißen zum Beispiel alle vier Optionsfelder in der Gruppe Alter »age«). Damit wird verhindert, dass mehr als ein Schalter in einer Gruppe zur Zeit ausgewählt werden kann. Das bedeutet wiederum, dass in der Eingabe für Ihr Skript nur ein Wert pro Gruppe erscheint. Sie müssen die Eingabe mit allen möglichen Werten vergleichen, um herauszufinden, welche Option gewählt wurde.

Das Skript

Das CGI-Skript, das dieses Formular verarbeitet, hat vier Hauptaufgaben zu erledigen:

Listing 16.3 zeigt den Code für unser CGI-Skript mit Namen umfrage.pl.

Listing 16.3: Das Skript umfrage.pl

1:  #!/usr/bin/perl -w
2: use strict;
3: use CGI qw(:standard);
4:
5: my $results = 'umfrage_ergebnisse.txt';
6: my %data = ();
7: my $thing = '';
8: my $val = 0;
9:
10: open(RESULTS, $results) or
die "Ergebnisdatei konnte nicht geoeffnet werden: $!";
11: while (<RESULTS>) {
12: ($thing, $val) = split(' ');
13: $data{$thing} = $val;
14: }
15: close(RESULTS);
16:
17: # Gesamtsumme
18: $data{total}++;
19:
20: # Alter
21: if (!param('age')) { $data{age_na}++ }
22: else {
23: if (param('age') eq 'under18') { $data{age_under18}++; }
24: elsif (param('age') eq '18to34') { $data{age_18to34}++; }
25: elsif (param('age') eq '35to50') { $data{age_35to50}++; }
26: elsif (param('age') eq '50plus') { $data{age_50plus}++; }
27: }
28:
29: # Geschlecht
30: if (!param('sex')) { $data{sex_na}++ }
31: else {
32: if (param('sex') eq 'male') { $data{sex_m}++; }
33: elsif (param('sex') eq 'female') { $data{sex_f}++; }
34: }
35:
36: # Perl
37: if (!param('perl')) { $data{perl_na}++ }
38: else {
39: if (param('perl') eq 'yes') { $data{perl_y}++; }
40: elsif (param('perl') eq 'no') { $data{perl_n}++; }
41: }
42:
43: open(RESULTS, ">$results") or
die "In Ergebnisdatei kann nicht geschrieben werden: $!";
44: foreach $thing (keys %data) {
45: print RESULTS "$thing $data{$thing}\n";
46: }
47: close(RESULTS);
48:
49: print header;
50: print start_html('Danke');
51: print <<EOF;
52: <H1>Danke, dass Sie das Umfrageformular ausgefüllt haben!</H1>
53: <P>Die bisherigen Ergebnisse:
54: <P>Geschlecht:
55: <TABLE BORDER><TR><TH>Männlich</TH><TD>
56: EOF
57:
58: print &percent('sex_m'), "</TD></TR>\n";
59: print "<TR><TH>Weiblich</TH><TD>\n";
60: print &percent('sex_f'), "</TD></TR>\n";
61: print "<TR><TH>Keine Antwort</TH><TD>\n";
62: print &percent('sex_na'), "</TD></TR>\n";
63: print "</TABLE>\n";
64:
65: print "<P>Alter:\n";
66: print "<TABLE BORDER><TR><TH>Unter 18</TH><TD>\n";
67: print &percent('age_under18'), "</TD></TR>\n";
68: print "<TR><TH>18 bis 34</TH><TD>\n";
69: print &percent('age_18to34'), "</TD></TR>\n";
70: print "<TR><TH>35 bis 50</TH><TD>\n";
71: print &percent('age_35to50'), "</TD></TR>\n";
72: print "<TR><TH>Über 50</TH><TD>\n";
73: print &percent('age_50plus'), "</TD></TR>\n";
74: print "<TR><TH>Keine Antwort</TH><TD>\n";
75: print &percent('age_na'), "</TD></TR>\n";
76: print "</TABLE>\n";
77:
78: print "<P>Perl-Programmierer?\n";
79: print "<TABLE BORDER><TR><TH>Ja</TH><TD>\n";
80: print &percent('perl_y'), "</TD></TR>\n";
81: print "<TR><TH>Nein</TH><TD>\n";
82: print &percent('perl_n'), "</TD></TR>\n";
83: print "<TR><TH>Keine Antwort</TH><TD>\n";
84: print &percent('perl_na'), "</TD></TR>\n";
85: print "</TABLE>\n";
86:
87: print end_html;
88:
89: sub percent {
90: if (defined $data{$_[0]}) {
91: return sprintf("%.1f%%", $data{$_[0]} / $data{total} * 100);
92: }
93: else { return '0%'; }
94: }

Ich möchte dieses Skript nicht Zeile für Zeile durchgehen, da Sie einen großen Teil davon schon in irgendeiner Form gesehen haben (und viele Zeilen nur aus print- Anweisungen bestehen). Statt dessen möchte ich Sie auf einige der wichtigeren Teile dieses Skripts aufmerksam machen:

Zeile 5 speichert den Namen der Datei mit den Umfragedaten in der Variablen $results. Dabei wird davon ausgegangen, dass sich die Datei in dem gleichen Verzeichnis befindet wie das CGI-Skript. Gegebenenfalls müssen Sie den Pfadnamen in Ihrem Skript ändern. Die Datendatei besteht aus einem Satz von Datenschlüsseln für die einzelnen Optionen in der Umfrage und »na«-Schlüsseln für den Fall, dass in einer Gruppe keine Option ausgewählt wurde. Jeder Schlüssel hat einen Wert für die Anzahl der »Stimmen«, die eingereicht wurden. In den Zeilen 10 bis 15 wird die Datendatei geöffnet und die Daten in den Hash %data eingelesen. Anschließend wird die Datei direkt wieder geschlossen.

Die Datendatei - die hier im gleichen Verzeichnis steht wie das CGI-Skript - muss dem Webserver den Schreibzugriff erlauben. Für Unix-Systeme bedeutet dies, dass die Datei mit den entsprechenden Zugriffsberechtigungen versehen sein muss, so dass der Webserver mit seiner Benutzer- oder Gruppen-ID (in der Regel nobody) darauf zugreifen kann. Aber auch unter Windows müssen Sie Ihre Sicherheitseinstellungen entsprechend setzen. Wenn Sie das Skript auf einem Webserver ausführen und dabei Fehler erhalten, die besagen, dass in die Datei nicht geschrieben werden konnte (und die bei der Ausführung von der Befehlszeile aus nicht aufgetreten sind), sollten Sie die Zugriffsberechtigungen für die Datei prüfen.

Die Zeilen 17 bis 41 verarbeiten die Eingabe des Formulars in Gruppen, die mit den Gruppen der Optionsschalter in der HTML-Datei (Alter, Geschlecht und Perl) übereinstimmen. Beachten Sie, dass Sie für jede Gruppe testen müssen, ob eventuell keine Antwort gegeben wurde (in diesem Falle wird nämlich für diese Gruppe kein Schlüssel in dem param()-Array, das Sie von CGI.pm erhalten, eingerichtet.) Wurde eine Auswahl getroffen, inkrementieren wir den Wert dieses Schlüssels in dem Daten- Hash. Dabei verfolgen wir die Gesamtsumme (Zeile 17), die wir benötigen, um die Prozentwerte für die Ausgabe zu berechnen.

Nachdem wir die Daten verarbeitet haben, können wir die neue Datendatei in die gleiche Datei schreiben, je einen Datenschlüssel und -wert pro Zeile, getrennt durch ein Leerzeichen. Die eigentliche Reihenfolge der Schlüssel ist nicht von Belang, da sie vom Skript nur wieder aus der Datei in einen Hash gelesen werden. Eine einfache foreach-Schleife in den Zeilen 44 und 45 schreibt die neuen Werte in die Datendatei (die wir in Zeile 43 zum Schreiben neu geöffnet haben).

Die zweite Hälfte des Skripts erzeugt als Ausgabe das aktuelle Ergebnis der Umfrage, die der Benutzer als Antwort erhält. Ab Zeile 49 erfolgt die Ausgabe, die mit dem Header beginnt (Zeile 49). Anschließend werden der Titel (Zeile 50) und mit Hilfe eines Hier-Dokuments (Zeile 51 bis 56) die ersten Zeilen HTML-Text ausgegeben.

Ab Zeile 58 wird das Ganze dann erst richtig interessant. Die restliche HTML-Datei besteht aus Tabellen, in denen die aktuellen Ergebnisse der Umfrage präsentiert werden. Ein Großteil der folgenden print-Ausgabe wird für den HTML-Code für die Tabellen benötigt. Die endgültigen Prozentzahlen werden mit einer von uns definierten Hilfsroutine namens &percent() berechnet. Die in den Zeilen 89 bis 94 definierte &percent()-Routine erzeugt einen Prozent-String, der sich auf der Basis des ihr übergebenen Werts geteilt durch die Gesamtzahl der Antworten errechnet. Diese Routine stellt auch sicher, dass der gegebene Datenschlüssel tatsächlich einen Wert hat (ist er gleich Null, erscheint er nicht im Hash %data). Und zum Schluß formatiert sie mit Hilfe der Funktion sprintf die Prozentangaben mit einem Dezimalpunkt und einem Prozentzeichen (beachten Sie, dass Sie aufgrund des sprintf-Formatiercodes nicht ohne weiteres ein einfaches Prozentzeichen ausgeben können; Sie müssen es als %% eingeben).

Vertiefung

Wie ich bereits zu Beginn dieser Lektion erwähnt habe, könnte ich problemlos Seite um Seite und Kapitel um Kapitel mit all den verschiedenen Aspekten von CGI füllen. Eine genaue Beschreibung von CGI.pm allein wäre schon wesentlich umfangreicher als mein bescheidenes Kapitel hier. Dennoch gibt es einige Besonderheiten von CGI.pm, auf die ich noch näher eingehen möchte. Alle diese Besonderheiten finden Sie natürlich in der Dokumentation zu CGI.pm (perldoc CGI weist Ihnen den Weg) und auf der Webseite http://www.genome.wi.mit.edu/ftp/pub/software/WWW/ cgi_docs.html.

Wenn Sie an weitergehenden Informationen zu CGI selbst interessiert sind, scheuen Sie sich nicht, die Webseiten zu besuchen, die ich eingangs dieser Lektion erwähnt habe, oder lesen Sie das Buch, das ich Ihnen dort empfohlen habe. Wenn Sie Unterstützung brauchen, um Ihre CGI-Skripts auf verschiedenen Plattformen zum Laufen zu bringen, schauen Sie doch mal in der Usenet-Newsgroup comp.infosystems.www.authoring.cgi nach.

CGI-Variablen verwenden

Wenn Ihr CGI-Skript mit den Daten vom Browser aufgerufen wird, hält der Webserver in seiner Umgebung einige interessante Werte bereit, die sich auf das Skript selbst, auf den Webserver und auf das System, auf dem der Browser läuft, der das Formular abgeschickt hat, beziehen. Auf Unix-Systemen werden diese Werte auch Umgebungsvariablen genannt, auf die man innerhalb des Perl-Skripts mit dem Hash %ENV zugreifen kann. Auf anderen Webservern erfolgt die Übergabe dieser Variablen unter Umständen anders. In CGI.pm jedoch finden Sie Subroutinen, um auf diese Variablen in einer Art und Weise zuzugreifen, die plattform- und Webserver- unabhängig ist. Sie müssen diese Subroutinen nicht unbedingt in Ihren CGI-Skripten verwenden, aber vielleicht finden Sie die Daten ganz nützlich.

Tabelle 16.1 enthält die CGI.pm-Subroutinen zu den CGI-Variablen.

Subroutine

Was Sie dadurch erhalten

accept()

Eine Liste der MIME-Typen, die der Browser akzeptiert

auth_type()

Den Authentifizierungstyp (normalerweise »basic«)

path_info()

Pfadinformationen kodiert im Skript-URL (falls verwendet)

path_translated()

Das gleiche wie path_info(), erweitert zu einem vollen Pfadnamen

query_string()

Argumente für das CGI-Skript, die an den URL angehängt wurden

raw_cookie()

Liefert reine Cookie-Informationen. Verwalten Sie diese Informationen mit den cookie()-Subroutinen

referer()

Den URL der Seite, die dieses Skript aufgerufen hat

remote_addr()

Die IP-Adresse des Hosts, der dieses Skript aufgerufen hat (der Host des Browsers)

remote_ident()

Die User-ID, aber nur wenn das System ident ausführt (nicht üblich)

remote_host()

Der Name des Hosts, der dieses Skript aufgerufen hat

remote_user()

Der Name des Benutzers, der dieses Skript aufgerufen hat (normalerweise nur gesetzt, wenn der Benutzer sich mit Authentifizierung eingeloggt hat)

request_method()

Die Webserver-Methode, mit der das Skript aufgerufen wurde (zum Beispiel GET oder POST)

script_name()

Der Name des Skripts

server_name()

Der Hostname des Webservers, der dieses Skript aufgerufen hat

server_software()

Der Name und die Version der Webserver-Software

virtual_host()

Für Server, die virtuelle Hosts unterstützen, der Name des virtuellen Hosts, der dieses Skript ausführt

server_port()

Der Netzwerk-Port, den der Server verwendet (normalerweise 80)

user_agent()

Name und Version des Browsers, der dieses Skript aufgerufen hat, zum Beispiel Mozilla/4.x (Win95)

user_name()

Der Name des Benutzers, der dieses Skript aufgerufen hat (fast nie gesetzt)

Tabelle 16.1: Die Subroutinen zu den CGI-Variablen  

POST und GET

Ein Browser kann CGI-Skripts auf zwei Wegen aufrufen: via POST oder via GET. GET kodiert die Formularelemente direkt in den URL, während POST die Formularelemente über die Standardeingabe sendet. Darüber hinaus kann GET dazu verwendet werden, um ein Formular abzuschicken, das eigentlich nicht vorhanden ist - Sie könnten zum Beispiel einen Link haben, bei dem der URL die hartkodierten Formularelemente zum Abschicken enthält.

CGI.pm verarbeitet beide Wege und speichert sie in dem Parameter-Array Ihres CGI- Skripts, so dass Sie sich keine Gedanken darüber machen müssen, welche Methode zum Abschicken des Skripts verwendet wurde. Wenn Sie wirklich daran interessiert sind, die Parameter aus dem URL herauszulesen, verwenden Sie statt param() die Funktion url_param().

Redirektion

Manchmal ist das Ergebnis eines CGI-Skripts keine HTML-Datei, sondern statt dessen ein Verweis auf eine existierende HTML-Datei auf dem eigenen oder einem beliebigen Server. CGI.pm unterstützt diese Art von Ergebnis durch die Subroutine redirect():

print redirect('http://www.andererserver.com/anderedatei.html');

Mit redirect teilen Sie dem Browser des Benutzers mit, dass er nicht irgendeinen HTML-Code auf dem Bildschirm anzeigen, sondern eine bestimmte Datei im Web suchen soll. Da ein CGI-Skript, das redirect aufruft, keine neue Webseite erzeugt, sollten Sie in der Ausgabe eines CGI-Skripts redirect nicht mit irgendeinem HTML- Code kombinieren.

Cookies und das Hochladen von Dateien

Mit CGI.pm können Sie sogar Cookie-Werte verwalten und Dateien bearbeiten, die über den Upload-Mechanismus der HTML-Formulare hochgeladen werden. Zur Verwaltung von Cookies dient die Subroutine cookie() aus CGI.pm. Das Hochladen von Dateien verläuft ähnlich wie die Bearbeitung normaler Formularelemente. Die Subroutine param() wird verwendet, um den Dateinamen zurückzuliefern, der in dem Formular eingegeben wurde. Dieser Dateiname ist gleichzeitig auch ein Datei-Handle, der offen ist und aus dem Sie Zeilen mit den Standard-Perl-Mechanismen auslesen können.

Zu beiden Themen finden Sie weitere Informationen in der CGI.pm-Dokumentation.

CGI-Skripts und Sicherheit

Jedes CGI-Skript stellt ein potentielles Sicherheitsloch auf Ihrem Webserver dar. Jede beliebige Person, die durch das Web streift, kann CGI-Skripts auf Ihrem Server mit irgendwelchen Eingaben ausführen lassen. Je nachdem wie sorglos Sie Ihre Skripts schreiben und wie entschlossen die Gegenpartei ist, kann Ihr CGI-Skript für böswillige Nutzer Schlupflöcher aufweisen, die diese nutzen können, um in Ihr System einzubrechen und schlimmstenfalls unwiderruflichen Schaden anzurichten.

Der beste Weg, ein Problem zu beseitigen, besteht darin, es zu erkennen und Schritte zu seiner Vermeidung zu unternehmen. Ein empfehlenswerter Ansatzpunkt sind die häufig gestellten Fragen zur Sicherheit im WWW unter http://www.genome.wi.mit.edu/ WWW/faqs/www-security-faq.html. Perl verfügt außerdem über eine besondere Option, den sogenannten Taint-Modus, der Sie daran hindert, unsichere Daten so zu verwenden, dass Ihr System Schaden erleiden könnte. Im Kapitel 20, »Was noch bleibt«, erfahren Sie mehr über den Taint-Modus.

Perl in den Webserver einbetten

Jedesmal, wenn ein in Perl geschriebenes CGI-Skript von einem Webserver aufgerufen wird, erfolgt ein Aufruf an Perl, um das Skript auszuführen. Für sehr stark frequentierte Webserver, auf denen eine Unmenge von CGI-Skripten ausgeführt wird, kann das bedeuten, dass viele Kopien von Perl gleichzeitig aufgerufen werden, was eine beträchtliche Last für die Maschine darstellt, die als Webserver fungiert. Um ihre Leistungsfähigkeit zu verbessern, verfügen viele Webserver über einen Mechanismus, mit dem der Perl-Interpreter in dem Webserver selbst eingebettet wird, so dass Skripts, die auf dem Webserver aufgerufen werden, nicht länger als CGI-Skripts ausgeführt werden. Statt dessen werden sie als Teile der Webserver-Bibliothek ausgeführt, wodurch der Overhead und die Anlaufzeit für die Skripts reduziert werden. Oftmals muss man die CGI-Skripts nicht einmal anpassen, um sie auf diese Weise ausführen zu lassen.

Unterschiedliche Webserver auf unterschiedlichen Plattformen verwenden unterschiedliche Mechanismen zur Einbettung von Perl. Sie müssen in der Dokumentation zu Ihrem Webserver nachlesen, ob mit Perl verfaßte CGI-Skripts eingebettet werden können und welche Tools und Module man dafür benötigt.

Wenn Sie einen ISAPI-Webserver unter Windows (beispielsweise den IIS) verwenden, benötigen Sie das »Perl für ISAPI«-Paket (manchmal auch PerlIIS genannt). Dieses Paket ist Teil der ActiveState-Version von Perl für Windows und wird automatisch zusammen mit diesem Paket installiert. Sie können es aber auch separat von der ActiveState-Web-Site unter http://www.activestate.com herunterladen.

Wenn Sie einen Open-Source Apache-Webserver verwenden, finden Sie in mod_perl ein Apache-Modul, das den Perl-Interpreter in den Apache-Webserver einbettet. Damit erreichen Sie, dass Ihre in Perl geschriebenen CGI-Skripts schneller und effizienter ausgeführt werden. Doch obwohl dies sicherlich das wichtigste Ergebnis ist, ist es nicht das einzige. Darüber hinaus gestattet Ihnen die Einbettung fortan, mit Perl auf alle internationalen Erweiterungs-APIs von Apache zuzugreifen, so dass Sie Ihren Webserver mit Hilfe von Perl in nahezu unbegrenzter Weise anpassen können. Weitere Informationen zum Apache-Webserver finden Sie unter http:// www.apache.org und beim Apache/Perl-Integrationsprojekt, den Entwicklern von mod_perl, und zwar unter http://perl.apache.org.

Zusammenfassung

Nicht alles in Perl ist gleichbedeutend mit langen, komplizierten Skripten und vielen, vielen Subroutinen. Manchmal sind es gerade die nützlichsten und interessantesten Dinge, die man mit Hilfe von Modulen und ein wenig eigenem Code, der das Skript zusammenhält, implementieren kann. CGI ist dafür ein hervorragendes Beispiel. Das Modul CGI.pm übernimmt den Großteil der harten Arbeit bei der CGI-Programmierung und macht es leicht, die Werte aus einem Formular oder einer anderen CGI-Quelle auszulesen und eine HTML-Datei als Ergebnis zurückzusenden.

Heute haben Sie ein wenig darüber gelernt, wie Perl für CGI eingesetzt werden kann. Sie wissen jetzt, wie CGI abläuft - vom Browser zum Server zum Skript und wieder zurück -, wie CGI.pm in Ihr Skript importiert und dort genutzt werden kann und wie die zahlreichen Möglichkeiten dieses Moduls Ihr Leben mit CGI angenehmer machen. Wir haben ein umfangreicheres CGI-Beispiel gesehen: ein Umfrageformular, das die Umfragedaten in einer externen Datei abspeichert. Ich kann zwar nicht garantieren, dass diese Lektion Ihnen genügend Wissen vermittelt hat, um CGI-Skripts für beliebige Aufgaben zu erstellen (weil ich dazu zuviel in dieser Lektion auslassen mußte), aber ich hoffe, dass sich die Lektion als eine gute Ausgangsbasis erweist, auf der Sie aufbauen können.

Die in dieser Lektion vorgestellten Subroutinen sind alle Teil der CGI.pm-Moduls und umfassen:

Fragen und Antworten

Frage:
Ich habe eine Reihe von CGI-Skripten gesehen, die in Perl geschrieben waren und trotzdem keinen Gebrauch von CGI.pm gemacht haben. Statt dessen verwenden diese Skripts andere Bibliotheken wie z.B. cgi-lib.pl. Sind diese Skripts schlecht und sollten korrigiert werden?

Antwort:
Nicht unbedingt. Es gibt im Web etliche Bibliotheken zur Erstellung von CGI- Skripten mit Perl, und cgi-lib.pl ist eine der bekanntesten. Sie können bedenkenlos diese anderen Bibliotheken verwenden. Ich habe mich in diesem Kapitel für CGI.pm entschieden, da es inzwischen der Standard für Perl im Zusammenspiel mit CGI geworden ist. Es ist ein unproblematisches Modul, das den Standard-Modul-Konventionen folgt, bei Bedarf objektorientiert genutzt werden kann und, was am wichtigsten ist, Teil der Standard-Perl- Version ist. Damit hat es einen unschlagbaren Vorteil gegenüber anderen Bibliotheken.

Frage:
Meine CGI-Skripts lassen sich von der Befehlszeile aus ausführen, nicht jedoch von einer Webseite. Was mache ich falsch?

Antwort:
In Anbetracht der vielen verschiedenen Plattformen und Webserver, die es gibt, läßt sich diese Frage nicht so einfach beantworten. Ist das Skript eine ausführbare Datei? Ist das Skript in einem »offizielles« CGI-Verzeichnis Ihres Servers abgelegt (normalerweise ein spezielles cgi-bin-Verzeichnis oder etwas Ähnliches)? Sind die Zugriffsberechtigungen für Ihr Skript korrekt gesetzt (Webserver führen CGI-Skripts als einen besonderen Benutzer mit begrenzten Zugriffsrechten aus)? Sind Sie sicher, dass auf Ihrer Maschine überhaupt ein Webserver installiert ist? Auf den Web-Sites, die ich Ihnen im Laufe dieser Lektion genannt habe, finden Sie Tipps und Vorschläge zum Debuggen Ihrer CGI-Skripts.

Frage:
Meine Website befindet sich auf einer Unix-Maschine. Dort kann ich CGI-Skripts ausführen. Aber ich möchte meine CGI-Skripts lieber auf meinem Windows-NT- PC zuhause schreiben und debuggen, so dass ich nicht die ganze Zeit eingeloggt sein muss. Ist dies möglich?

Antwort:
Dank des Wunders der Plattformunabhängigkeit von Perl und des Moduls CGI.pm ist das selbstverständlich möglich. Sie müssen dazu einen Webserver auf Ihrem NT-System installieren - am besten einer der ähnlich oder gleich dem auf der Unix-Maschine ist. Danach können Sie lokal CGI-Skripts installieren, debuggen und ausführen. Sie sollten allerdings auf Unterschiede in den Pfadangaben der Dateien achten und auf die Art, in der verschiedene Server mit CGI umgehen. Halten Sie Ihr Skript so einfach wie möglich und Sie sollten wenig Arbeit damit haben, es auf Unix zu übertragen.

Frage:
Ich habe die Umfragedatei modifiziert, so dass sie jetzt mit einem Gästebuch- ähnlichen Skript verwendet wird und es den Besuchern erlaubt, Kommentare zu einer Webseite abzugeben. Allerdings habe ich Probleme damit, wenn mehrere Besucher zur selben Zeit ihren Kommentar an die Webseite schicken. Manchmal wird von mehreren Besuchern gleichzeitig in die Datei geschrieben, und Kommentare gehen dadurch verloren, oder seltsame Dinge passieren. Was mache ich falsch?

Antwort:
Das Umfragebeispiel in diesem Kapitel zeigt nur die Grundlagen der CGI- Skripterstellung. Um kommerzielle Web-Sites zu unterstützen, müssen Sie schon etwas tiefer einsteigen (ich empfehle, die von mir bereits genannten Websites zu konsultieren und ein weiterführendes Buch anzuschaffen). Besonders, wenn Sie mit einer externen Datei arbeiten, die potentiell von mehreren Benutzern gleichzeitig beschrieben werden kann - wie das mit jedem CGI-Skript möglich ist -, werden Sie diese Datei »sperren« müssen, bevor in die Datei geschrieben wird, und anschließend die Sperre wieder aufheben. Das könnte so einfach sein, wie eine weitere temporäre Datei namens umfrage.lock anzulegen. In Ihrem Skript würden Sie also, bevor Sie in die Datei mit den Umfrageergebnissen schreiben, Folgendes tun:

Workshop

Der Workshop enthält Quizfragen, die Ihnen helfen sollen, Ihr Wissen zu festigen, und Übungen, die Sie anregen sollen, das eben Gelernte umzusetzen und eigene Erfahrungen zu sammeln. Versuchen Sie, das Quiz und die Übungen zu beantworten und zu verstehen, bevor Sie zur Lektion des nächsten Tages übergehen.

Quiz

  1. Wofür steht CGI? Wozu wird es verwendet?
  2. Welche Unterstützung bietet Ihnen das Modul CGI.pm? Wo finden Sie es, und wie nutzen Sie es?
  3. Warum sollten Sie ein CGI-Skript erst über die Befehlszeile ausführen, bevor Sie es über eine Webseite ausführen lassen?
  4. Wie setzen Sie die Subroutine param() ein?
  5. Nennen Sie drei Möglichkeiten, HTML-Code als Ausgabe Ihres CGI-Skripts zu erzeugen.
  6. Warum ist ein Hier-Dokument manchmal sinnvoller als print-Anweisungen?

Übungen

  1. Schreiben Sie ein CGI-Skript, das die Formularelemente, die ihm übergeben wurden, als Liste mit Aufzählungszeichen ausgibt. HINWEIS: Listen mit Aufzählungszeichen haben in HTML folgendes Format:
        <UL>
    <LI>Ein Element
    <LI>Zwei Elemente
    <LI>Drei Elemente
    </UL>
  2. Ein weiterer Hinweis: param() ohne Argumente gibt Ihnen eine Liste der Namen aller Formularelemente aus, die an das Skript geschickt wurden.
  3. FEHLERSUCHE: Was ist falsch an folgendem Skriptfragment:
        if (!param()) { $data{sex_na}++ }
    else {
    if (param() eq 'male') { $data{sex_m}++; }
    elsif (param() eq 'female') { $data{sex_f}++; }
    }
  4. Schreiben Sie ein CGI-Skript, das eine einfache HTML-Seite ausgibt (»Hallo Welt« reicht), gleichzeitig registriert, wie oft auf die Seite zugegriffen wurde, und diesen Wert oben auf der gleichen Seite anzeigt.
  5. Schreiben Sie ein CGI-Skript, um ein sehr einfaches »Gästebuch« zu implementieren, das es den Benutzern erlaubt, einzeilige Kommentare an eine Webseite zu schicken. Registrieren Sie alle vergangenen Kommentare. Gehen Sie dabei von der Existenz eines HTML-Formulars mit zwei Elementen aus: einem Textfeld namens Mail für die E-Mail-Adresse des Absenders und einem Textfeld namens Kommentar für die Kommentare.

Antworten

Hier die Antworten auf die Workshop-Fragen aus dem vorigen Abschnitt.

Antworten zum Quiz

  1. CGI steht für Common Gateway Interface (Allgemeine Zugangsschnittstelle) und bezieht sich auf Programme, die auf einem Webserver als Antwort auf Anfragen von einem Webbrowser (meist von einem Formular auf einer HTML-Seite) ausgeführt werden.
  2. Das Modul CGI.pm hilft beim Schreiben, Ausführen und Debuggen von CGI- Skripten. Es definiert eine Reihe von Subroutinen, mit denen die Ein- und Ausgaben der CGI-Skripts bearbeitet werden können, sowie etliche Hilfsroutinen zu fast jedem erdenklichen Aspekt der CGI-Skripterstellung.
  3. Das Modul CGI.pm ist Teil der meisten aktuellen Perl-Versionen. Wenn Sie nicht schon bereits darüber verfügen, können Sie es von dem weiter vorn in dieser Lektion genannten URL herunterladen und auf Ihrem Computer installieren.
  4. Um das CGI.pm-Modul zu nutzen, müssen Sie es, wie jedes andere Modul auch, erst einmal importieren. Dazu verwenden Sie use und ein Import-Tag wie :standard oder :all.
  5. Ein CGI-Skript von der Befehlszeile auszuführen, macht es in der Regel leichter, Syntax- oder sonstige Fehler zu finden (so brauchen Sie das Skript nicht erst auf dem Webserver zu installieren, Sie müssen keine Verbindung zum Internet herstellen und so weiter). Indem Sie dem Skript bei Ausführung von der Befehlszeile Musterdaten übergeben, können Sie es testen, bevor Sie es online verfügbar machen.
  6. Die Subroutine param() dient dazu, die Werte aller Formularelemente auf der Webseite, die das Skript aufgerufen hat, zu ermitteln. Ohne Argumente aufzurufen, liefert param() eine Liste der Schlüssel zurück, die sich auf die Namen der Formularelemente beziehen. Mit einem Schlüsselargument (einem String) aufgerufen, liefert param() den Wert zurück, der mit diesem Formularelement verbunden ist.
  7. HTML-Code können Sie auf eine der folgenden Arten ausgeben:
  1. Hier-Dokumente stellen eine Möglichkeit dar, einen Block von Text wörtlich auszugeben. Alle Neue-Zeile-Zeichen und Anführungszeichen werden unverändert ausgegeben. Hier-Dokumente helfen Ihnen, eine Menge von sich wiederholenden print-Anweisungen sowie Anführungszeichen mit Escape-Zeichen zu vermeiden.

Antworten zu den Übungen

  1. Hier ein Lösungsvorschlag:
        #!/usr/bin/perl -w
    use strict;
    use CGI qw(:standard);

    my @keys = param();

    print header;
    print start_html('Hallo!');
    print "<H1>Schlüssel/Wert-Paare</H1>\n";
    print "<UL>\n";

    foreach my $name (@keys) {
    print "<LI>$name = ", param($name), "\n";
    }
    print "</UL>\n";

    print end_html;
  2. Die Subroutine param() wird ohne irgendwelche Argumente aufgerufen. In einigen Fällen - wie zum Beispiel in Übung 1 - entspricht dies genau Ihren Wünschen. In diesem Fall jedoch, in dem die Tests einen String-Wert erwarten, sollte param() besser mit einer Art von String-Argument aufgerufen werden, um einen Wert aus den Formularparametern zu erhalten.
  3. Hier ein Lösungsvorschlag (vergessen Sie nicht vorher zaehlert.txt zu erzeugen):
        #!/usr/bin/perl -w
    use strict;
    use CGI qw(:standard);

    my $countfile = 'zaehler.txt';
    my $count = '';

    open(COUNT, $countfile) or
    die "Zählerdatei konnte nicht geöffnet werden: $!";
    while (<COUNT>) {
    $count = $_;
    }
    close(COUNT);

    $count++;

    open(COUNT, ">$countfile") or
    die "In Zählerdatei kann nicht geschrieben werden: $!";
    print COUNT $count;
    close(COUNT);

    print header;
    print start_html('Hallo!');
    print "<H1>Hallo Welt</H1>\n";
    print "<P>Diese Seite wurde bereits $count Mal besucht.\n";

    print end_html;
  4. Hier ein (besonders einfacher) Lösungsvorschlag. Dieses Skript geht von einer Datei mit bereits abgegebenen Kommentaren aus, pro Zeile einer, mit der E-Mail- Adresse vorneweg:
        #!/usr/bin/perl -w
    use strict;
    use CGI qw(:standard);

    my $guestbook = 'gaeste.txt';
    my $mail = ''; # EMail-Adresse
    my $comment = ''; # Kommentare

    open(GUEST, $guestbook) or
    die "Gästebuch konnte nicht geöffnet werden: $!";

    print header;
    print start_html('Kommentare');
    print "<H1>Kommentare</H1>\n";
    print "<P>Kommentare zu dieser Website!\n";
    print "<HR>\n";

    while (<GUEST>) {
    ($mail, $comment) = split(' ',$_,2);
    if ($mail) {
    if (!$comment) { $comment = "nichts\n"; }
    else { print "<P><B>$mail sagt:</B> $comment"; }
    }
    }

    $mail = param('mail');
    $comment = param('comment');
    print "<P><B>$mail sagt:</B> $comment\n";

    open(GUEST, ">>$guestbook") or
    die "Gästebuch konnte nicht geöffnet werden: $!";
    print GUEST "$mail $comment\n";

    print end_html;


vorheriges KapitelInhaltsverzeichnisStichwortverzeichnisFeedbacknächstes Kapitel


© Markt&Technik Verlag, ein Imprint der Pearson Education Deutschland GmbH